# To change this template, choose Tools | Templates
# and open the template in the editor.

__author__="jonathan"
__date__ ="$16-May-2010 17:39:32$"

import numpy
from math import radians, sin, cos, tan, asin

class IsometricView(object):

    def __init__(self, angle=radians(45), tilt=asin(tan(radians(30))), offset=(0,0,0), center=(0,0,0)):
        self._angle = angle
        self._tilt = tilt
        self._offset = numpy.array(offset)
        self._center = numpy.array(center)
        self._matrix = None
        self._inv_matrix = None
        self._dirty = False
        self._proj_line = None
        self._gen_matrix()

    @property
    def matrix(self):
        if self._matrix is None or self._dirty:
            self._gen_matrix()
        return self._matrix

    @property
    def inv_matrix(self):
        if self._inv_matrix is None or self._dirty:
            self._gen_matrix()
        return self._inv_matrix

    @property
    def angle(self):
        return self._angle

    @angle.setter
    def angle(self, val):
        self._angle = val
        self._dirty = True

    @property
    def tilt(self):
        return self._tilt

    @tilt.setter
    def tilt(self, val):
        self._tilt = val
        self._dirty = True

    @property
    def offset(self):
        return self._offset

    @offset.setter
    def offset(self, val):
        self._offset = numpy.array(val)
        self._dirty = True

    @property
    def center(self):
        return self._center

    @center.setter
    def center(self, val):
        self._center = numpy.array(val) * (1,1,0)
        self._o = (self._offset - numpy.dot(self.matrix, self._center))

    def _gen_matrix(self):
        rot_x = numpy.array(
            ((1, 0,              0              ),
             (0, cos(self.tilt), -sin(self.tilt)),
             (0, sin(self.tilt),  cos(self.tilt)))
         )
        rot_z = numpy.array(
            ((cos(self.angle), -sin(self.angle), 0),
             (sin(self.angle),  cos(self.angle), 0),
             (0,                0,               1))
         )
        self._matrix = numpy.dot(rot_x, rot_z)
        self._inv_matrix = numpy.dot(numpy.transpose(rot_z), numpy.transpose(rot_x))
        self._proj_line = numpy.dot(self._inv_matrix, numpy.array((0,0,1)))
        self._proj_line /= self._proj_line[2]
        self._dirty = False
        self._o = (self._offset - numpy.dot(self.matrix, self._center))

    def to_view(self, points):
        iso_points = []
        for p in points:
            # rotate from world coords into iso coords
            iso_p = numpy.dot(self.matrix, numpy.array(p))
            # offset to give screen coords
            iso_p += self._o
            iso_points.append(iso_p)
        return iso_points

    def from_view(self, iso_points):
        points = []
        for iso_p in iso_points:
            # move from screen coords to iso coords
            p = numpy.array(iso_p) - self._o
            # rotate from iso coords into world coords
            p = numpy.dot(self.inv_matrix, p)
            # we now need to project our point onto the xy plane using the
            # screen z-axis, so this projection won't effect the screen coord
            # of the point.
            p -= (self._proj_line * p[2])
            points.append(p)
        return points
